/*******************************************************************************
 * Copyright (c) 2012 March and Conquest.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package mrusanov.towers.main;

import java.util.Collections;
import java.util.List;

import mrusanov.towers.cards.AbstractCard;
import mrusanov.towers.configuration.GamefieldConfiguration;
import mrusanov.towers.net.TurnInfo;
import mrusanov.towers.net.TurnInfo.TurnDecision;
import mrusanov.towers.players.Player;
import mrusanov.towers.players.RemotePlayer;
import mrusanov.towers.resources.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

public class Game {
	private static final Logger LOG = LoggerFactory.getLogger(Game.class);

	private final List<Player> players;
	private final Player thisPlayer, enemyPlayer;
	private Player currentMovingPlayer;
	private int turnNumber = 1;
	private final GamefieldConfiguration configuration;

	public Game(String thisPlayerName, String anotherPlayerName, GamefieldConfiguration configuration) {

		thisPlayer = new Player(thisPlayerName);
		enemyPlayer = new RemotePlayer(anotherPlayerName);

		players = Lists.newArrayList(thisPlayer, enemyPlayer);

		this.configuration = configuration;

		initCards();
		initResources();
		initTowers();
	}

	private void initTowers() {
		for (Player player : players) {
			player.getTower().setWallHeight(configuration.getInitialWall());
			player.getTower().setTowerHeight(configuration.getInitialTower());
		}
	}

	private void initCards() {
		for (int i = 0; i < configuration.getCardsNumber(); i++) {
			thisPlayer.takeNewCard();
		}
	}

	private void initResources() {
		for (Player player : players) {
			for (Resource resource : Resource.values()) {
				player.getIncome().setResource(resource, configuration.getInitialIncome());
				player.getResources().setResource(resource, configuration.getInitialResources());
			}
		}
	}

	public List<Player> getPlayers() {
		return Collections.unmodifiableList(players);
	}

	Player getAnotherPlayer(Player current) {
		Preconditions.checkArgument(players.contains(current), "Invalid player parameter");

		if (players.get(0) != current) {
			return players.get(0);
		} else {
			return players.get(1);
		}
	}

	public void initFirstMovingPlayer(Player player) {
		Preconditions.checkArgument(players.contains(player), "Invalid player parameter");

		currentMovingPlayer = player;
		players.remove(player);
		players.add(0, player);
	}

	public Player nextMove() {
		currentMovingPlayer = getAnotherPlayer(currentMovingPlayer);
		if (players.indexOf(currentMovingPlayer) == 0) {
			turnNumber++;
			for (Player player : players) {
				player.addIncome();
			}
		}

		LOG.debug("Next move invoked. Current moving player is " + currentMovingPlayer + ", turn number is "
				+ turnNumber);
		return currentMovingPlayer;
	}

	public Player getCurrentMovingPlayer() {
		return currentMovingPlayer;
	}

	public Player getThisPlayer() {
		return thisPlayer;
	}

	public Player getEnemyPlayer() {
		return enemyPlayer;
	}

	public int getTurnNumber() {
		return turnNumber;
	}

	public void playCard(AbstractCard card) {
		LOG.debug("Player " + getCurrentMovingPlayer() + " played card " + card);
		getCurrentMovingPlayer().getResources().payResourceCost(card.getCost());
		getCurrentMovingPlayer().playCard(this, card);
		if (!card.playAgain()) {
			nextMove();
		}
	}

	public void throwCard(AbstractCard card) {
		LOG.debug("Player " + getCurrentMovingPlayer() + " threw card " + card);
		getCurrentMovingPlayer().throwCard(card);
		nextMove();
	}

	public GameResult getGameResult() {
		if (!thisPlayer.getTower().isAlive() && !enemyPlayer.getTower().isAlive()) {
			return GameResult.BOTH_PLAYERS_DEFEATED;
		}

		if (!thisPlayer.getTower().isAlive()) {
			return GameResult.THIS_PLAYER_DEFEATED;
		}

		if (!enemyPlayer.getTower().isAlive()) {
			return GameResult.ENEMY_PLAYER_DEFEATED;
		}

		if (thisPlayer.getTower().getTowerHeight() >= configuration.getTowerHeightToWin()) {
			return GameResult.THIS_PLAYER_RAISED_TOWER;
		}

		if (enemyPlayer.getTower().getTowerHeight() >= configuration.getTowerHeightToWin()) {
			return GameResult.ENEMY_PLAYER_RAISED_TOWER;
		}

		return GameResult.GAME_IN_PROGRESS;
	}

	public Player getPreviouslyMovedPlayer(TurnInfo info) {
		Player movedPlayer = null;
		Player currentPlayer = getCurrentMovingPlayer();
		Player notCurrentPlayer = getAnotherPlayer(currentPlayer);

		if (info.getTurnDecision() == TurnDecision.PLAY_CARD) {
			if (info.getCard().playAgain()) {
				movedPlayer = currentPlayer;
			} else {
				movedPlayer = notCurrentPlayer;
			}
		} else {
			movedPlayer = notCurrentPlayer;
		}
		return movedPlayer;
	}
}
